package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import org.springframework.beans.factory.DisposableBean;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;

/**
 * JaCoCo运行时配置类
 * 用于在应用程序运行时收集代码覆盖率数据
 * 当应用程序关闭时，会将收集到的覆盖率数据保存到文件中
 */
@Configuration
public class JacocoConfig implements DisposableBean {

    /**
     * JaCoCo运行时代理
     */
    private Object jacocoRuntime;

    /**
     * 初始化JaCoCo运行时代理
     * 该方法会在应用程序启动时被调用
     * @return JaCoCo运行时代理对象
     */
    @Bean
    public Object jacocoRuntimeBean() {
        try {
            // 通过反射获取JaCoCo运行时代理
            Class<?> rtClass = Class.forName("org.jacoco.agent.rt.RT");
            Method getAgentMethod = rtClass.getMethod("getAgent");
            Object jacocoRuntime = getAgentMethod.invoke(null);
            this.jacocoRuntime = jacocoRuntime;
            
            // 设置JaCoCo排除规则
            try {
                // 设置包含和排除规则
                Method setSessionIdMethod = jacocoRuntime.getClass().getMethod("setSessionId", String.class);
                // 设置会话ID，同时包含排除规则
                // 排除所有Hibernate相关类和ByteBuddy类，以及所有以$开头的内部类
                String sessionId = "includes=com.example.*:excludes=java.*:javax.*:sun.*:com.sun.*:org.hibernate.*:net.bytebuddy.*:*$HibernateProxy*:*$HibernateInstantiator*:*$ByteBuddy*:*$EnhancerBy*";
                setSessionIdMethod.invoke(jacocoRuntime, sessionId);
                
                System.out.println("JaCoCo排除规则已更新: " + sessionId);
                System.out.println("注意：如果仍有插桩错误，请检查错误日志并添加相应的排除规则");
                
                System.out.println("成功设置JaCoCo排除规则: " + sessionId);
            } catch (Exception e) {
                System.out.println("设置JaCoCo排除规则失败: " + e.getMessage());
                e.printStackTrace();
            }
            
            return jacocoRuntime;
        } catch (Exception e) {
            System.out.println("无法初始化JaCoCo运行时代理: " + e.getMessage());
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 应用程序关闭时保存覆盖率数据
     * 该方法会在应用程序关闭时被调用
     */
    @Override
    public void destroy() throws Exception {
        if (jacocoRuntime != null) {
            try {
                // 创建target目录（如果不存在）
                java.io.File targetDir = new java.io.File("target");
                if (!targetDir.exists()) {
                    targetDir.mkdirs();
                }
                
                // 获取覆盖率数据
                byte[] data = (byte[]) jacocoRuntime.getClass().getMethod("getExecutionData", boolean.class)
                        .invoke(jacocoRuntime, false);
                
                // 保存覆盖率数据到文件
                try (FileOutputStream out = new FileOutputStream("target/jacoco-runtime.exec")) {
                    out.write(data);
                }
                
                System.out.println("JaCoCo运行时覆盖率数据已保存到: target/jacoco-runtime.exec");
            } catch (Exception e) {
                System.err.println("保存JaCoCo覆盖率数据时出错: " + e.getMessage());
                e.printStackTrace();
            }
        }
    }

    /**
     * 手动触发保存覆盖率数据的方法
     * 可以通过API调用此方法来保存当前的覆盖率数据
     * @return 保存是否成功
     */
    public boolean dumpJacocoData() {
        if (jacocoRuntime == null) {
            return false;
        }
        
        try {
            // 创建target目录（如果不存在）
            java.io.File targetDir = new java.io.File("target");
            if (!targetDir.exists()) {
                targetDir.mkdirs();
            }
            
            // 获取覆盖率数据
            byte[] data = (byte[]) jacocoRuntime.getClass().getMethod("getExecutionData", boolean.class)
                    .invoke(jacocoRuntime, false);
            
            // 保存覆盖率数据到文件
            try (FileOutputStream out = new FileOutputStream("target/jacoco-runtime.exec")) {
                out.write(data);
            }
            
            System.out.println("JaCoCo运行时覆盖率数据已手动保存到: target/jacoco-runtime.exec");
            return true;
        } catch (Exception e) {
            System.err.println("手动保存JaCoCo覆盖率数据时出错: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
}